/*
 * This class handles creating randomly generated levels based on size and difficulty.
 * The randomness has rules, such as a HOUSE Cube cannot be fully blocked, the Cubes at the
 * entrance and exit must connect, and a certain amount of obstacles per difficulty.
 * Difficulty is given as an integer value (from 1 - 4) and is mapped accordingly:
 * 1 = easy, 2 = normal, 3 = hard, 4 = very hard.
 * A level also holds the player's score for this level.
 */

#pragma once
#include "GameApplication.h"
#include "Grid.h"
#include "Cube.h"
#include <vector>
#include "OgreMaterial.h"

#define NUM_MATERIALS 20

class Grid;
class Cube;

extern Ogre::SceneManager* sceneMgr; // Defined in main.cpp

class Level
{		
	enum MaterialID
	{
		NW2NE, NW2E, NW2SW, NW2S, NW2SE, N2W, N2E, N2SW, N2S, N2SE, NE2W, NE2SW, NE2S, NE2SE, W2E, W2S, W2SE, E2SW, E2S, SW2SE
	};
	private:
		std::list<Ogre::MaterialPtr> materials;    // master material list
		Ogre::Real levelTime; //the total time, in milliseconds, taken in this level
		bool finished;
		int direction;		//The direction from previousCube to currentCube
		int nextDirection;	//The direction from currentCube to nextCube
		int difficulty;		//The difficulty of this Level
		int rows;			//The number of rows in this Level
		int columns;		//The number of columns in this Level
		int area;			//The area of the Level (rows*columns)
		int score;			//The current score for this Level
		int numHouses;		//The randomly generated number of houses in this Level
		int numObstacles;	//The randomly generated number of obstacles in this Level
		Cube* start;		//The beginning cube that the player starts on
		Cube* end;			//The cube that the player will end on
		Cube* begin;		//The cube that is adjacent to the start cube, but off the grid
		Cube* finish;		//The cube that is adjacent to the end cub, but off the grid
		Cube* currentCube;	//The Cube that the player is currently working with
		Cube* previousCube;	//The Cube that the player previously worked on
		Cube* nextCube;		//The Cube that the player will be going to next, this is updated by updateNext() which is called in nextIsntObstacle
		std::list<GridNode*> path;			//The path through this level
		Ogre::MeshPtr cubePtr;
		Grid* grid;			//The grid that represents this Level 
		//Ogre::MaterialPtr	NW2NE, NW2E, NW2SW, NW2S, NW2SE, N2W, N2E, N2SW, N2S, N2SE, NE2W, NE2SW, NE2S, NE2SE, W2E, W2S, W2SE, E2SW, E2S, SW2SE;
		std::list<Cube*> cubeList;	//List that holds all Cubes in the level
		void generateHouseAndObstacleNums(); //A helper method for the Constructor
		std::list<GridNode*> createPath();	//Randomly creates a path through the given level, sets path to the path created
		void setupLevel( std::list<GridNode*>* points, std::list<GridNode*>* obstaclePoints); //Randomly sets up the 
									//level based on the path and conditions that will allow the path to be traversed 
									//uninhibited
		std::list<GridNode*> Level::generateObstaclePoints();
		//std::string materials[6];
		bool areConnected();		//Checks to make sure that the currentCube and the previousCube are connected.  The currentCube and previousCube must be of type HouseCube or PipeCube.
									//This also assumes that the two cubes are adjacent
		bool nextIsntObstacle();	//Checks to make sure that the next Cube that we're going to isn't an ObstacleCube and also updates nextCube
		void placeStartAndEndPipes();	//This places the pipes that connect to the start and the end cubes... this does not place the pipecubes that are denoted by start and end, but rather the ones that are off the grid and are either left, right, up, or down from start or end
		void setupMaterials();
	public:
		Level(); //Generates a completely random level (random difficulty and size)
		Level(int difficulty);	//Generates a random level based on difficulty.  The size of the level will also be random.
		Level(int difficulty, int rows, int columns); //Generates a random level based on difficulty and the size of rows and columns.
		~Level();	//My destructor
		void updateScore();	//Makes the level calculate the player's score
		int retrieveScore(){return score;};	//Returns the score for this level (this should be called after the level has completed)
		std::list<Cube*>* getCubeList(); //returns a pointer to all the Cubes in the Level
		void update( Ogre::Real deltaTime );	//Updates the Level, includes updating the timers on the Houses, checking the connectiveness of the currentCube, updating the score, etc.
		void updateCurrentCube( Cube* newCurrent );	//Updates the current Cube in the Level to the one passed and updates the previous Cube to the previous current Cube.
													//This also updates the direction from the new previous Cube to the new current Cube
		Cube* getStartCube();	//Returns the starting cube.  This is for GameApplication so that it knows we need to move the starting cube
		Cube* checkNextConnectivity();	//Checks if the previous and current Cubes are connected and goes to the next Cube if they are connected.  Returns the current cube resulting from this call.
		static enum Directions {N, NE, E, SE, S, SW, W, NW};	//Cardinal directions that are used on the grid
		bool isFinished(){return finished;}
};